(算法)金钗赛诗
题目:
赛诗会后,十二金钗待奔前程。分别宴上,12人各写了一首诗放入包囊。
大家随机取一个,若取到自己的诗,则再取一个,并放回自己的诗,12人都拿到诗算一种分配。
请问:共有多少种不同的分配?
思路:
问题简化:给定n个人写n首诗,要求赠给其他人,共有多少种分配方法。
通俗一点就是:1到n的全排列,第i个数不是i的排列共有多少种?
其实这就是“错位排列”。
(错位排列问题的答案可以由“错排公式”求得,关于错排公式的推导可以参考百度百科:http://baike.baidu.com/view/668994.htm)
这里简单从类似动态规划的角度来分析问题:
假设n个数的错位排列数目为dp[n]
第n个数可以放置在1...(n-1)之间的任何一个位置,共(n-1)种方法;
假设第n个数放在了第k个位置,那么对于数字k而言:
- 要么放在第n个位置;
- 要么不放在第n个位置;
如果放在了第n个位置,那么相当于n和k做了交换,剩下的就是其他(n-2)个数的错位排列了,即dp[n-2];
如果不放在第n个位置,那么剩余(n-1)个数(可以将此时k看出原来的n,即此时剩下除了k位置外的所有数),即dp[n-1];
因此动态规划的状态转移方程为:
dp[n]=(n-1)*(dp[n-1]+dp[n-2]);
初始状态:
当n=1;dp[1]=0;
当n=2;dp[2]=1;(两个数,只存在一种错排的可能)
通过上述公式的递推可以得到“错排公式”:
D(n) = n! [1/0! - 1/1! + 1/2! - 1/3! + 1/4! + ... + (-1)^n/n!].
代码:
#include <iostream> using namespace std; int miscombination_1(int n){ int A[n+1]; A[1]=0; A[2]=1; for(int i=3;i<=n;i++) A[i]=(i-1)*(A[i-1]+A[i-2]); return A[n]; } int miscombination_2(int n){ if(n<=2) return n-1; int first=0; int second=1; int third=0; for(int i=3;i<=n;i++){ third=(i-1)*(first+second); first=second; second=third; } return third; } int main() { cout << miscombination_1(12) << endl; cout << miscombination_2(12) << endl; return 0; }
问题答案:
176214841